﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;

namespace Elentok.Common.Helpers
{
  /// <summary>
  /// Contains binary-related helper methods
  /// </summary>
  public static class BinaryHelper
  {
    /// <summary>
    /// Returns true if this stream is UTF8 (if it starts with 0xEF, 0xBB, 0xBF)
    /// (seeks back to the beginning of the stream when done)
    /// </summary>
    /// <param name="stream"></param>
    /// <typeparam name="TStream">The type of the stream</typeparam>
    /// <returns></returns>
    public static bool IsUtf8<TStream>(this TStream stream)
      where TStream : Stream
    {
      byte[] preamble = Encoding.UTF8.GetPreamble();

      try
      {
        int b = stream.ReadByte();
        if (b != -1 && b == preamble[0])
        {
          b = stream.ReadByte();
          if (b != -1 && b == preamble[1])
          {
            b = stream.ReadByte();
            if (b != -1 && b == preamble[2])
              return true;
          }
        }
        return false;
      }
      finally
      {
        stream.Seek(0, SeekOrigin.Begin);
      }
    }

    /// <summary>
    /// Reads all of the bytes from the given stream
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static byte[] ReadAllBytes(this Stream stream)
    {
      if (stream == null)
        throw new ArgumentNullException("stream");

      int bufferSize = 1024;
      using (MemoryStream memoryStream = new MemoryStream())
      {
        while (true)
        {
          byte[] buffer = new byte[bufferSize];
          int bytesRead = stream.Read(buffer, 0, bufferSize);
          if (bytesRead > 0)
            memoryStream.Write(buffer, 0, bytesRead);

          if (bytesRead < bufferSize)
            return memoryStream.ToArray();
        }
      }
    }

    /// <summary>
    /// Generates a random byte array
    /// </summary>
    /// <param name="size"></param>
    /// <returns></returns>
    public static byte[] GenerateRandomByteArray(int size)
    {
      byte[] bytes = new byte[size];
      Random random = new Random();
      for (int i = 0; i < size; i++)
        bytes[i] = (byte)random.Next(0, Byte.MaxValue + 1);

      return bytes;
    }

    /// <summary>
    /// Resizes the array to remove the zeros at the end
    /// </summary>
    /// <param name="data"></param>
    [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", 
      Justification="This method uses the Array.Resize method which requires a reference to the byte array")]
    public static void TrimRightZeroPadding(ref byte[] data)
    {
      if (data[data.Length - 1] != 0)
        return;

      int i = data.Length - 1;
      for (; i >= 0; i--)
      {
        if (data[i] != 0)
          break;
      }

      Array.Resize(ref data, i + 1);
    }

    /// <summary>
    /// Converts a byte array to an uppercase hexadecimal string
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string ToHexString(this byte[] value)
    {
      StringBuilder builder = new StringBuilder();
      foreach (byte b in value)
        builder.Append(b.ToString("x2", CultureInfo.InvariantCulture).ToUpperInvariant());

      return builder.ToString();
    }
  }
}
